"""
Technical indicators calculation service
"""

import pandas as pd
import pandas_ta as ta
import numpy as np
from typing import Dict, List, Optional, Tuple
from datetime import datetime

from app.models.market_data import TechnicalIndicator
from app.core.database import SessionLocal


class TechnicalIndicatorService:
    """Service for calculating technical indicators"""
    
    def __init__(self):
        self.indicators_config = {
            'moving_averages': {
                'sma_20': {'type': 'sma', 'length': 20},
                'sma_50': {'type': 'sma', 'length': 50},
                'sma_200': {'type': 'sma', 'length': 200},
                'ema_12': {'type': 'ema', 'length': 12},
                'ema_26': {'type': 'ema', 'length': 26},
            },
            'momentum': {
                'rsi_14': {'type': 'rsi', 'length': 14},
                'macd': {'type': 'macd', 'fast': 12, 'slow': 26, 'signal': 9},
                'stoch_k': {'type': 'stoch', 'k': 14, 'd': 3, 'smooth_k': 3},
            },
            'volatility': {
                'bb_upper': {'type': 'bbands', 'length': 20, 'std': 2},
                'atr_14': {'type': 'atr', 'length': 14},
            },
            'volume': {
                'volume_sma_20': {'type': 'sma', 'source': 'volume', 'length': 20},
                'obv': {'type': 'obv'},
            }
        }
    
    def calculate_all_indicators(self, df: pd.DataFrame) -> pd.DataFrame:
        """Calculate all technical indicators for given price data"""
        
        # Make a copy to avoid modifying original data
        data = df.copy()
        
        # Moving Averages
        data['sma_20'] = ta.sma(data['close'], length=20)
        data['sma_50'] = ta.sma(data['close'], length=50)
        data['sma_200'] = ta.sma(data['close'], length=200)
        data['ema_12'] = ta.ema(data['close'], length=12)
        data['ema_26'] = ta.ema(data['close'], length=26)
        
        # Momentum Indicators
        data['rsi_14'] = ta.rsi(data['close'], length=14)
        
        # MACD
        macd = ta.macd(data['close'], fast=12, slow=26, signal=9)
        if macd is not None:
            data['macd'] = macd['MACD_12_26_9']
            data['macd_signal'] = macd['MACDs_12_26_9']
            data['macd_histogram'] = macd['MACDh_12_26_9']
        
        # Bollinger Bands
        bb = ta.bbands(data['close'], length=20, std=2)
        if bb is not None:
            data['bb_upper'] = bb['BBU_20_2.0']
            data['bb_middle'] = bb['BBM_20_2.0']
            data['bb_lower'] = bb['BBL_20_2.0']
        
        # Volatility
        data['atr_14'] = ta.atr(data['high'], data['low'], data['close'], length=14)
        
        # Volume Indicators
        data['volume_sma_20'] = ta.sma(data['volume'], length=20)
        data['obv'] = ta.obv(data['close'], data['volume'])
        
        # Custom indicators
        data = self._calculate_custom_indicators(data)
        
        return data
    
    def _calculate_custom_indicators(self, df: pd.DataFrame) -> pd.DataFrame:
        """Calculate custom technical indicators"""
        data = df.copy()
        
        # Volume Weighted Moving Average (VWMA)
        data['vwma_20'] = (data['close'] * data['volume']).rolling(window=20).sum() / data['volume'].rolling(window=20).sum()
        
        # Price Volume Trend (PVT)
        data['pvt'] = ((data['close'] - data['close'].shift(1)) / data['close'].shift(1) * data['volume']).cumsum()
        
        # Money Flow Index (MFI)
        typical_price = (data['high'] + data['low'] + data['close']) / 3
        money_flow = typical_price * data['volume']
        
        positive_flow = money_flow.where(typical_price >= typical_price.shift(1), 0).rolling(window=14).sum()
        negative_flow = money_flow.where(typical_price < typical_price.shift(1), 0).rolling(window=14).sum()
        
        money_ratio = positive_flow / negative_flow
        data['mfi_14'] = 100 - (100 / (1 + money_ratio))
        
        # Williams %R
        data['williams_r'] = self._williams_r(data['high'], data['low'], data['close'], 14)
        
        # Average Directional Index (ADX)
        adx = ta.adx(data['high'], data['low'], data['close'], length=14)
        if adx is not None:
            data['adx_14'] = adx['ADX_14']
            data['plus_di'] = adx['DMP_14']
            data['minus_di'] = adx['DMN_14']
        
        return data
    
    def _williams_r(self, high: pd.Series, low: pd.Series, close: pd.Series, period: int = 14) -> pd.Series:
        """Calculate Williams %R indicator"""
        highest_high = high.rolling(window=period).max()
        lowest_low = low.rolling(window=period).min()
        
        wr = -100 * ((highest_high - close) / (highest_high - lowest_low))
        return wr
    
    def generate_signals(self, df: pd.DataFrame) -> Dict[str, any]:
        """Generate trading signals based on technical indicators"""
        data = df.copy()
        latest = data.iloc[-1]
        
        signals = {
            'trend': self._analyze_trend(data),
            'momentum': self._analyze_momentum(latest),
            'volatility': self._analyze_volatility(data),
            'volume': self._analyze_volume(data),
            'overall_score': 0,
            'recommendation': 'NEUTRAL'
        }
        
        # Calculate overall score
        signals['overall_score'] = (
            signals['trend']['score'] * 0.3 +
            signals['momentum']['score'] * 0.25 +
            signals['volatility']['score'] * 0.2 +
            signals['volume']['score'] * 0.25
        )
        
        # Generate recommendation
        if signals['overall_score'] > 0.7:
            signals['recommendation'] = 'STRONG_BUY'
        elif signals['overall_score'] > 0.3:
            signals['recommendation'] = 'BUY'
        elif signals['overall_score'] < -0.7:
            signals['recommendation'] = 'STRONG_SELL'
        elif signals['overall_score'] < -0.3:
            signals['recommendation'] = 'SELL'
        else:
            signals['recommendation'] = 'HOLD'
        
        return signals
    
    def _analyze_trend(self, df: pd.DataFrame) -> Dict[str, any]:
        """Analyze trend direction and strength"""
        latest = df.iloc[-1]
        
        # EMA crossover signals
        ema_bullish = latest['ema_12'] > latest['ema_26']
        sma_bullish = latest['sma_20'] > latest['sma_50']
        
        # Price position relative to moving averages
        price_above_sma20 = latest['close'] > latest['sma_20']
        price_above_sma50 = latest['close'] > latest['sma_50']
        
        # Trend strength
        trend_score = 0
        
        if ema_bullish:
            trend_score += 0.3
        if sma_bullish:
            trend_score += 0.3
        if price_above_sma20:
            trend_score += 0.2
        if price_above_sma50:
            trend_score += 0.2
            
        return {
            'score': trend_score,
            'direction': 'BULLISH' if trend_score > 0.5 else 'BEARISH' if trend_score < -0.5 else 'NEUTRAL',
            'ema_crossover': 'BULLISH' if ema_bullish else 'BEARISH',
            'sma_crossover': 'BULLISH' if sma_bullish else 'BEARISH'
        }
    
    def _analyze_momentum(self, latest: pd.Series) -> Dict[str, any]:
        """Analyze momentum indicators"""
        rsi = latest['rsi_14']
        macd = latest['macd'] if 'macd' in latest else 0
        macd_signal = latest['macd_signal'] if 'macd_signal' in latest else 0
        
        # RSI analysis
        rsi_oversold = rsi < 30
        rsi_overbought = rsi > 70
        
        # MACD analysis
        macd_bullish = macd > macd_signal and macd > 0
        
        momentum_score = 0
        
        if rsi_oversold:
            momentum_score += 0.5
        elif rsi_overbought:
            momentum_score -= 0.5
            
        if macd_bullish:
            momentum_score += 0.5
            
        return {
            'score': momentum_score,
            'rsi': rsi,
            'rsi_signal': 'OVERSOLD' if rsi_oversold else 'OVERBOUGHT' if rsi_overbought else 'NEUTRAL',
            'macd_signal': 'BULLISH' if macd_bullish else 'BEARISH'
        }
    
    def _analyze_volatility(self, df: pd.DataFrame) -> Dict[str, any]:
        """Analyze volatility and Bollinger Bands"""
        latest = df.iloc[-1]
        
        # Bollinger Bands position
        bb_position = (latest['close'] - latest['bb_lower']) / (latest['bb_upper'] - latest['bb_lower'])
        
        # ATR relative to price
        atr_ratio = latest['atr_14'] / latest['close']
        
        volatility_score = 0
        
        # Near lower band = potential buy
        if bb_position < 0.2:
            volatility_score += 0.5
        # Near upper band = potential sell
        elif bb_position > 0.8:
            volatility_score -= 0.5
            
        return {
            'score': volatility_score,
            'bb_position': bb_position,
            'atr_ratio': atr_ratio,
            'volatility_level': 'HIGH' if atr_ratio > 0.05 else 'LOW' if atr_ratio < 0.01 else 'MEDIUM'
        }
    
    def _analyze_volume(self, df: pd.DataFrame) -> Dict[str, any]:
        """Analyze volume indicators"""
        latest = df.iloc[-1]
        
        # Volume compared to average
        volume_ratio = latest['volume'] / latest['volume_sma_20']
        
        # OBV trend
        obv_trend = 'UP' if df['obv'].iloc[-5:].is_monotonic_increasing else 'DOWN'
        
        volume_score = 0
        
        if volume_ratio > 1.5 and obv_trend == 'UP':
            volume_score += 0.5
        elif volume_ratio < 0.8 and obv_trend == 'DOWN':
            volume_score -= 0.5
            
        return {
            'score': volume_score,
            'volume_ratio': volume_ratio,
            'obv_trend': obv_trend,
            'volume_signal': 'BULLISH' if volume_score > 0 else 'BEARISH' if volume_score < 0 else 'NEUTRAL'
        }


# Global instance
indicator_service = TechnicalIndicatorService()
